// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "image_manipulator.h"
#include "image.h"
#include "image_utilities.h"
#include "colorspace_conversions_ex.h"

using std::unique_ptr;

namespace harmonizer {

ImageManipulator::ImageManipulator(const std::string& file_name) :
            raw_image_(ReadImageOrDie(file_name)) {
  // Nothing to do here
}

ImageManipulator::ImageManipulator(unique_ptr<Image> raw_image) :
            raw_image_(std::move(raw_image)) {
  // Nothing to do here
}

// Just to test that I can do basic image manipulation
void ImageManipulator::NegateImage() {
  for (int i = 0; i < raw_image_->width(); ++i) {
    for (int j = 0; j < raw_image_->height(); ++j) {
      for (int k = 0; k < raw_image_->channels(); ++k) {
        raw_image_->SetValue(i, j, k,
                             kuint8max - raw_image_->GetValue(i, j, k));
      }
    }
  }
}

bool ImageManipulator::FitImageToHueRange(const HueRange& hue_range) {
  return FitImageToHueRange(hue_range, HueRange::LINEAR_SCALE);
}

bool ImageManipulator::FitImageToHueRange(
    const HueRange& hue_range, HueRange::HueFitMethod fit_method) {
  // This function changes the hue of each pixel to the nearest allowed value.
  // A better version of this would preserve some distance between different
  // values, instead of mapping them to the same value.

  FloatImage hsv_image;

  // Convert to HSV
  bool retval = ConvertRGBToHSVFloat(*raw_image_, &hsv_image);
  if (!retval) {
    return retval;
  }

  hue_range.FitToRange(fit_method, &hsv_image);

  // Convert back to RGB
  retval = ConvertHSVToRGBFloat(hsv_image, raw_image_.get());
  if (!retval) {
    return retval;
  }

  return true;
}

unique_ptr<Image> ImageManipulator::GetRawImageCopy() const {
  unique_ptr<Image> retval(new Image());
  retval->CopyFrom(*raw_image_);
  return retval;
}

// image_manipulators are equal iff underlying images are equal
bool ImageManipulator::Equals(const ImageManipulator& other) const {
  return raw_image_->Equals(*other.GetRawImageCopy());
}

bool ImageManipulator::WriteImageToFile(
    const std::string& file_name) {
  return WriteImage(*raw_image_, file_name);
}

bool ImageManipulator::CreateLabelImage(const HueRange& hue_range,
                                        Image* outputImage) const {
  // default lambda value
  const float kLambda = 1;

  return CreateLabelImage(hue_range, outputImage, kLambda);
}

bool ImageManipulator::CreateLabelImage(const HueRange& hue_range,
                                        Image* outputImage,
                                        float lambda) const {
  FloatImage hsv_image;

  // We only handle 2 labels for now (although this can be easily extended)
  CHECK(hue_range.NumberOfSegments() <= 2);

  // Color table for our output. line i holds the RGB values for label i.
  const uint8 labels_to_rgb[][3] = {
      {255,   0,   0},
      {  0, 255,   0}
  };

  // Convert to HSV
  bool retval = ConvertRGBToHSVFloat(*raw_image_, &hsv_image);
  if (!retval) {
    return retval;
  }

  Array2D<uint8> labels;
  hue_range.AssignToSegmentUsingMincut(hsv_image, lambda, &labels);

  // Populate the RawImage according to 'labels'
  outputImage->Resize(labels.width(), labels.height(), Image::RGB);
  for (int i = 0; i < labels.width(); ++i) {
    for (int j = 0; j < labels.height(); ++j) {
      int index = labels(j, i);
      outputImage->SetValue(i, j, 0, labels_to_rgb[index][0]);
      outputImage->SetValue(i, j, 1, labels_to_rgb[index][1]);
      outputImage->SetValue(i, j, 2, labels_to_rgb[index][2]);
    }
  }

  return true;
}

ImageManipulator::~ImageManipulator() {
  // nothing to do here
}

}  // namespace harmonizer
